package com.ehotting.edsta.circuitDesign.gen.circuit;


import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.ehotting.eaf.redis.service.RedisService;
import com.ehotting.edsta.circuitDesign.bean.AllMapBean;
import com.ehotting.edsta.circuitDesign.bean.Constant;
import com.ehotting.edsta.circuitDesign.domain.EsCircuit;
import com.ehotting.edsta.circuitDesign.domain.EsCircuitCodeData;
import com.ehotting.edsta.circuitDesign.enums.CircuitTypeEnum;
import com.ehotting.edsta.circuitDesign.enums.CodeDataTypeEnum;
import com.ehotting.edsta.circuitDesign.gen.DataTypeConvertHelper;
import com.ehotting.edsta.circuitDesign.gen.PropertyTipPackageHelper;
import com.ehotting.edsta.circuitDesign.gen.ScriptLogger;
import com.ehotting.edsta.circuitDesign.gen.circuit.helper.CircuitScriptHelper;
import com.ehotting.edsta.circuitDesign.groovy.Groovy;
import com.ehotting.edsta.circuitDesign.service.IEsCircuitCodeDataService;
import com.ehotting.edsta.circuitDesign.service.IEsCircuitService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 电路选择引擎
 */
@Service
public class CircuitScriptEngine {

    private Logger logger = LoggerFactory.getLogger(CircuitScriptEngine.class);

    @Autowired
    private CircuitScriptFactory circuitScriptFactory;
    @Autowired
    private IEsCircuitCodeDataService esCircuitCodeDataService;
    @Autowired
    private CircuitScriptHelper circuitScriptHelper;
    @Autowired
    private Groovy groovy;
    @Autowired
    private RedisService redisService;
    @Autowired
    private IEsCircuitService esCircuitService;
    public List<EsCircuit> invoke(Long circuitTypeId,
                                  Map<String,Object> context,
                                  AllMapBean all,
                                  String mainProjectId){

        PropertyTipPackageHelper.TipCollectionIds tipCollectionIds = redisService.getCacheObject(Constant.CIRCUIT_TIP_IDS + mainProjectId);
        Long[] idsArray = selectIds(tipCollectionIds,circuitTypeId);
        List<EsCircuit> allTypeCircuitList = esCircuitService.seEsCircuitByIds(idsArray);

        logger.info("Start CircuitScriptEngine.");

        logger.info("allTypeCircuitList.size():{}",allTypeCircuitList.size());
        List<EsCircuit> modelCircuitList = new ArrayList<>();

        for (EsCircuit circuit : allTypeCircuitList) {
            /**
             * isEqualCircuit( circuit, context,all)执行groovy脚本
             */
            if (isEqualCircuit(circuit, context, all)) {
                modelCircuitList.add(circuit);
            }
            all.clearProp(circuit.getCircuitType().getCircuitType());
            if(circuitTypeId == 3 && modelCircuitList.size() > 1) break;
        }
        List<EsCircuit> resultCircuitList = modelCircuitList;
        if(circuitTypeId == 2){
            resultCircuitList =  modelToResult(modelCircuitList);
        }
        logger.info("resultCircuitList.size():{}",resultCircuitList.size());
        logger.info("End CircuitScriptEngine.");
        return resultCircuitList;
    }

    /**
     * 减少脚本执行的电路数（太耗时了）
     */
    public Long[] selectIds(PropertyTipPackageHelper.TipCollectionIds tipCollectionIds,Long circuitTypeId){
        if(circuitTypeId == 1){
            HashSet<Long> allSensorIds = new HashSet<>();
            Set<String> keySet = tipCollectionIds.getSensorIds().keySet();
            for (String key : keySet){
                allSensorIds.addAll(tipCollectionIds.getSensorIds().get(key));
            }
            return allSensorIds.toArray(new Long[allSensorIds.size()]);
        }
        if(circuitTypeId == 2){
            List<Long> controllerIdsList = new ArrayList<>(tipCollectionIds.getControllerIds());
            // 只取90个处理器电路
            int ceil = (int)Math.ceil(controllerIdsList.size() / 90.0);
            List<Long> newList = IntStream.range(0, controllerIdsList.size())
                    .filter(i -> i % ceil == 0)
                    .mapToObj(controllerIdsList::get)
                    .collect(Collectors.toList());

            return newList.toArray(new Long[newList.size()]);
        }
        if(circuitTypeId == 3){
            List<Long> powerIdsList = new ArrayList<>(tipCollectionIds.getPowerIds());
            return powerIdsList.toArray(new Long[powerIdsList.size()]);
        }
        return null;
    }

    /**
     * 根据处理器类型选择处理器电路
     */
    public List<EsCircuit> modelToResult(List<EsCircuit> modelCircuitList){
        Map<String, List<EsCircuit>> esCircuitMap = new HashMap<>();
        for(EsCircuit e : modelCircuitList){
            String[] split = e.getName().replace(" ", "").split("-");
            String key = split.length > 1 ? split[1] : split[0];
            if(esCircuitMap.get(key) == null){
                esCircuitMap.put(key,new ArrayList<>());
            }
            esCircuitMap.get(key).add(e);
        }

        Set<String> set = esCircuitMap.keySet();
        if(set.size() == 1){
            return modelCircuitList.stream().limit(5).collect(Collectors.toList());
        }
        List<EsCircuit> reList = new ArrayList<>();
        final int len = set.size() < 4 ? 2 : 1;
        for (String k : set){
            reList.addAll(esCircuitMap.get(k).stream().limit(len).collect(Collectors.toList()));
        }

        //reList = reList.stream().limit(10).collect(Collectors.toList());
        return reList;

    }

    public String genScript(EsCircuit circuit, List<EsCircuitCodeData>  circuitCodeDataList){
        List<String> argumentList = circuitCodeDataList.stream().map(t->{
            return t.getCircuitCodeDataTemplate().getCode();
        }).collect(Collectors.toList());
        String methodBody = circuitScriptFactory.genMethod(circuit, argumentList);
        String callBody = circuitScriptFactory.genCall(circuit, argumentList);
        String mainScript = circuitScriptHelper.genMainMethod(circuit,methodBody,callBody,argumentList);
        return mainScript;
    }


    public boolean runMain(EsCircuit circuit,String script, List<EsCircuitCodeData>  circuitCodeDataList,
                           Map<String, Object> context,AllMapBean all,CircuitTypeEnum circuitType){
        context.put("cirName",circuit.getName());
        // 定义输入对象
        Object[] mainParam = genMainParam(circuitCodeDataList, context,all,circuitType);

        ScriptLogger.info("ARGS:{}",JSON.toJSONString(mainParam, SerializerFeature.DisableCircularReferenceDetect));

        Object result = false;
        try {
            result = groovy.runGroovyScript(script, circuitScriptHelper.genMainName(circuit.getId()), mainParam);
        }catch (Exception e){
            e.printStackTrace();
            ScriptLogger.error(e,script,mainParam);
        }

        ScriptLogger.info("RESULT:{}",result.toString());
        return result == Boolean.TRUE;
    }

    public boolean isEqualCircuit(EsCircuit circuit, Map<String,Object> context, AllMapBean all){

        List<EsCircuitCodeData>  circuitCodeDataList =  esCircuitCodeDataService.findByCircuitId(circuit.getId());
        String script =  genScript(circuit,circuitCodeDataList);

        return  runMain(circuit,script,circuitCodeDataList, context,all,circuit.getCircuitType().getCircuitType());
    }

    private Object[] genMainParam(List<EsCircuitCodeData>  circuitCodeDataList,
                                  Map<String, Object> context,
                                  AllMapBean all, CircuitTypeEnum circuitType){
        Map<String,Object> prop = new HashMap<>();
        //System.out.println("____circuitCodeDataList.size():"+circuitCodeDataList.size());
        // 定义脚本入参对象
        Object[] objectArr = new Object[circuitCodeDataList.size() + 2];
        for (int i = 0; i < circuitCodeDataList.size(); i++){
            EsCircuitCodeData circuitCodeData = circuitCodeDataList.get(i);
            Object value = DataTypeConvertHelper.convertParamValue(circuitCodeData);
            objectArr[i] = value;
            prop.put(circuitCodeData.getCircuitCodeDataTemplate().getCode(),value);
            //System.out.println(circuitType.getCode()+"__"+circuitType.getName()+"__"+circuitCodeData.getCircuitCodeDataTemplate().getCode()+"++++"+value);
        }
        all.put(circuitType, CodeDataTypeEnum.PROP ,prop);

        objectArr[objectArr.length - 2] = context;
        objectArr[objectArr.length - 1 ] = all;
        return objectArr;
    }

}